#ifndef __ILIB_CORE_SOCKET_H_20120202__
#define __ILIB_CORE_SOCKET_H_20120202__

#include "../iHeader.h"
#include "../util/h/iSingleton.h"
#include "../util/h/iAllocator.h"
#include "../util/h/iThread.h"
#include "../util/h/iThreadMgr.h"
#include "../util/h/iIO.h"

#if defined(__WIN__)
typedef SOCKET os_socket_t;
#define ESO_WOULDBLOCK	WSAEWOULDBLOCK
#define ESO_EAGAIN		WSAEINPROGRESS
#elif defined(__LINUX__)
typedef int os_socket_t;
#define ESO_WOULDBLOCK	EWOULDBLOCK
#define ESO_EAGAIN		EAGAIN
#endif

class soctx : public iAllocator::Object
{
public:
	enum PROTOCOL { TCP=1, UDP };
    static soctx* new_instance(const char* address, iAllocator* alloc=iSingleton<iAllocator>::instance());
	soctx& operator=(const soctx& ctx);
    void release();

    inline int get_family() { return m_family; }
    inline PROTOCOL get_protocol() { return m_protocol; }
    inline sockaddr_in& get_sa4() { return m_addr.sa4; }
    inline sockaddr_in6& get_sa6() { return m_addr.sa6; }

private:
    soctx(iAllocator* alloc);
	soctx(PROTOCOL protocol, const char* ip, const char* port, iAllocator* alloc);
    ~soctx();

private:
	typedef union { sockaddr_in sa4; sockaddr_in6 sa6;} SOCKADDR;
	int			m_family;
	PROTOCOL	m_protocol;
	SOCKADDR    m_addr;
};

class iMsgDB;
class iClientSocket;
class iSocket : public iIO
{
public:
	class iSocketSink
	{
	public:
		virtual void on_connect_confirm() = 0;
		virtual void on_connect_indication(iClientSocket* cs) = 0;
		virtual void on_data_indication(iMsgDB* mdb) = 0;
        virtual void on_write() = 0;
		virtual void on_close() = 0;
	};

    inline bool is_udp() { return (m_local->get_protocol()&soctx::UDP) == soctx::UDP; }
	inline iSocketSink* set_sink(iSocketSink* sink) { iSocketSink* old=m_sink; m_sink = sink; return old;}

protected:
	iSocket(soctx* local, soctx* remote);
    virtual ~iSocket();

public:
	bool bind(soctx& ctx);
    int32_t send(iMsgDB* db);
    void close();

    inline iSocketSink* get_sink() { return m_sink; }

protected:
	soctx* m_local;
    soctx* m_remote;
	iSocketSink*	m_sink;
};

class iClientSocket : public iSocket
{
public:
	iClientSocket(soctx* local, soctx* remote);
    virtual ~iClientSocket();

    void connect();

    inline soctx* get_local()  { return m_local;  }
	inline soctx* get_remote() { return m_remote; }
	void attach(os_io_t io);

public:
	virtual int on_read();
	virtual int on_write();
	virtual int on_error();
};

class iListenSocket : public iSocket
{
public:
	iListenSocket(soctx* local);
    virtual ~iListenSocket();
	int32_t listen();
	
public:
	virtual int on_read();
	virtual int on_write();
	virtual int on_error();
};
#endif  //__ILIB_CORE_SOCKET_H_20120202__
